using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace IUPUI.StellarResults.Client.UIElements
{
	/// <summary>
	/// A view of an <see cref="Observatory"/>.
	/// </summary>
	public partial class ObservatoryView : UserControl
	{
		#region Private Properties
		private Simulation m_Simulation;
		private Observatory m_Observatory = null;
		private EventHandler<ININ.EventArgs<Observatory>> m_ObservatoryChanged;
		private DateTime m_CurrentDateTimeUTC;
		#endregion Private Properties

		#region Public Properties
		/// <summary>
		/// Gets or sets the current date time.
		/// </summary>
		/// <value>The current date time.</value>
		public DateTime CurrentDateTimeUTC
		{
			get
			{
				return m_CurrentDateTimeUTC;
			}
			set
			{
				if (m_CurrentDateTimeUTC != value)
				{
					m_CurrentDateTimeUTC = value;
					OnCurrentDateTimeChanged();
				}
			}
		}

		/// <summary>
		/// Gets or sets the simulation.
		/// </summary>
		/// <value>The simulation.</value>
		[Browsable(false)]
		public Simulation Simulation
		{
			get
			{
				return m_Simulation;
			}
			set
			{
				if (m_Simulation != value)
				{
					m_Simulation = value;
					OnSimulationChanged();
				}
			}
		}

		/// <summary>
		/// Gets or sets the observatory.
		/// </summary>
		/// <value>The observatory.</value>
		public Observatory Observatory
		{
			get
			{
				return m_Observatory;
			}
			set
			{
				if (m_Observatory != value)
				{
					m_Observatory = value;
					OnObservatoryChanged();
				}
			}
		}
		#endregion Public Properties

		#region Public Events
		/// <summary>
		/// Occurs when [observatory changed].
		/// </summary>
		public event EventHandler<ININ.EventArgs<Observatory>> ObservatoryChanged
		{
			add
			{
				m_ObservatoryChanged += value;
			}
			remove
			{
				m_ObservatoryChanged -= value;
			}
		}
		#endregion Public Events

		#region Constructors
		/// <summary>
		/// Initializes a new instance of the <see cref="ObservatoryView"/> class.
		/// </summary>
		public ObservatoryView()
		{
			InitializeComponent();

			m_GridDivisionsLabel.Enabled = 
				m_GridDivisionsTextBox.Enabled = 
				m_ShowGridCheckBox.Checked = m_TelescopeView.ShowGrid;
			m_GridDivisionsTextBox.Text = m_TelescopeView.GridDivisions.ToString();
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ObservatoryView"/> class.
		/// </summary>
		public ObservatoryView(
			IContainer container)
			: this()
		{
			container.Add(this);
		}
		#endregion Constructors

		/// <summary>
		/// Called when [simulation changed].
		/// </summary>
		protected virtual void OnSimulationChanged()
		{
			m_TelescopeView.Simulation = m_Simulation;

			m_Simulation.AstronomicalObjects.Modified += new EventHandler(AstronomicalObjects_Modified);
			OnAstronomicalObjectsChanged();
		}

		/// <summary>
		/// Handles the Modified event of the AstronomicalObjects control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void AstronomicalObjects_Modified(object sender, EventArgs e)
		{
			OnAstronomicalObjectsChanged();
		}

		/// <summary>
		/// Called when [astronomical objects changed].
		/// </summary>
		private void OnAstronomicalObjectsChanged()
		{
			// Clear list of trackable objects and add all astronomical objects to the list
			m_TrackComboBox.Items.Clear();
			m_TrackComboBox.Items.Add(Properties.Resources.TrackNoObjects);
			m_TrackComboBox.Items.AddRange(m_Simulation.AstronomicalObjects.ToArray());
			m_TrackComboBox.DisplayMember = "Name";

			m_TrackComboBox.SelectedIndex = 0;
		}

		/// <summary>
		/// Called when [observatory changed].
		/// </summary>
		protected virtual void OnObservatoryChanged()
		{
			m_TelescopeView.Observatory = m_Observatory;

			m_ObservatoryBindingSource.DataSource = m_Observatory;
			m_MapLinkLabel.Visible = 
				m_Observatory.Coordinates != null 
				&& m_Observatory.Coordinates.Latitude != 0 
				&& m_Observatory.Coordinates.Longitude != 0;

			m_AddressLabel.Text = 
				m_Observatory.Address.StreetAddress;
			m_Address2Label.Text =
				String.Format(
					"{0}, {1} {2} {3}",
					m_Observatory.Address.City,
					m_Observatory.Address.State,
					m_Observatory.Address.Zip,
					m_Observatory.Address.Country);
			if (m_Observatory.Coordinates != null)
			{
				m_CoordinatesLabel.Text =
					String.Format(
						Properties.Resources.CoordinatesFormat,
						m_Observatory.Coordinates.Latitude.ToString(),
						m_Observatory.Coordinates.Longitude.ToString());
			}

			m_AzimuthTextLabel.Text = m_Observatory.Altazimuth.Azimuth.ToString("N2");
			m_AltitudeTextLabel.Text = m_Observatory.Altazimuth.Altitude.ToString("N2");

			// Show or hide labels depending on if telescope is positionable
			m_NotPositionableLabel.Visible =
				!m_Observatory.IsPositionable;
			m_AzimuthTextLabel.Visible = 
				m_AzimuthLabel.Visible =
				m_AltitudeTextLabel.Visible =
				m_AltitudeLabel.Visible =
				m_MoveLinkLabel.Visible =
				m_TrackLabel.Visible =
				m_TrackComboBox.Visible =
				m_Observatory.IsPositionable;

			m_Observatory.ObservatoryPositionChanged += 
				new ObservatoryPositionChangeEventHandler(m_Observatory_ObservatoryPositionChanged);

			m_Observatory.AstronomicalObjectBeingTrackedChanged += 
				new EventHandler<ININ.EventArgs<string>>(m_Observatory_AstronomicalObjectBeingTrackedChanged);

			SetAstronomicalObjectBeingTracked();

			// Fire event
			if (m_ObservatoryChanged != null)
			{
				m_ObservatoryChanged(this, new ININ.EventArgs<Observatory>(m_Observatory));
			}
		}

		/// <summary>
		/// Handles the AstronomicalObjectBeingTrackedChanged event of the m_Observatory control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="ININ.EventArgs&lt;System.String&gt;"/> instance containing the event data.</param>
		private void m_Observatory_AstronomicalObjectBeingTrackedChanged(object sender, ININ.EventArgs<string> e)
		{
			foreach (object o in m_TrackComboBox.Items)
			{
				if (o is AstronomicalObject && ((AstronomicalObject)o).ID == e.Data)
				{
					m_TrackComboBox.SelectedItem = o;
					return;
				}
			}

			// If the item wasnt found, then assume we arent changing what we are tracking
		}

		/// <summary>
		/// Handles the ObservatoryPositionChanged event of the m_Observatory control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="IUPUI.StellarResults.ObservatoryPositionChangeEventArgs"/> instance containing the event data.</param>
		private void m_Observatory_ObservatoryPositionChanged(
			object sender, 
			ObservatoryPositionChangeEventArgs e)
		{
			if (!this.IsDisposed)
			{
				if (this.InvokeRequired)
				{
					try
					{
						this.Invoke(
							new ObservatoryPositionChangeEventHandler(m_Observatory_ObservatoryPositionChanged),
							sender,
							e);
					}
					catch
					{
						// ignore
					}
				}
				else
				{
					// Update the altitude and azimuth
					m_AltitudeTextLabel.Text = e.Altazimuth.Altitude.ToString("N2");
					m_AzimuthTextLabel.Text = e.Altazimuth.Azimuth.ToString("N2");

					// If observatory has moved and the simulation isnt running, we need to refresh the telescope's view
					if (!m_Simulation.IsSimulationRunning)
					{
						m_TelescopeView.Refresh();
					}
				}
			}
		}

		/// <summary>
		/// Called when [current date time changed].
		/// </summary>
		protected virtual void OnCurrentDateTimeChanged()
		{
			if (!IsDisposed)
			{
				if (this.InvokeRequired)
				{
					this.Invoke(new ININ.VoidEventHandler(OnCurrentDateTimeChanged));
				}
				else
				{
					m_CurrentDateTimeLabel.Text =
						m_CurrentDateTimeUTC.AddHours(m_Observatory.UTCOffset).ToString();
					m_TelescopeView.CurrentDateTimeUTC = m_CurrentDateTimeUTC;
				}
			}
		}

		/// <summary>
		/// Handles the CheckedChanged event of the m_ShowGridCheckBox control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void m_ShowGridCheckBox_CheckedChanged(
			object sender, 
			EventArgs e)
		{
			m_TelescopeView.ShowGrid = m_ShowGridCheckBox.Checked;

			m_GridDivisionsLabel.Enabled = m_GridDivisionsTextBox.Enabled = m_ShowGridCheckBox.Checked;
		}

		/// <summary>
		/// Handles the TextChanged event of the m_GridDivisionsTextBox control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void m_GridDivisionsTextBox_TextChanged(
			object sender, 
			EventArgs e)
		{
			int divisions = 0;
			if (Int32.TryParse(m_GridDivisionsTextBox.Text, out divisions))
			{
				m_TelescopeView.GridDivisions = divisions;
			}
		}

		/// <summary>
		/// Handles the LinkClicked event of the m_MapLlinkLabel control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.LinkLabelLinkClickedEventArgs"/> instance containing the event data.</param>
		private void m_MapLinkLabel_LinkClicked(
			object sender, 
			LinkLabelLinkClickedEventArgs e)
		{
			// Pop map url
			System.Diagnostics.Process.Start(
				String.Format(
					Properties.Resources.MapUrl,
					m_Observatory.Coordinates.ToString().Replace(" ", "")));
		}

		/// <summary>
		/// Handles the LinkClicked event of the m_MoveLinkLabel control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.LinkLabelLinkClickedEventArgs"/> instance containing the event data.</param>
		private void m_MoveLinkLabel_LinkClicked(
			object sender, 
			LinkLabelLinkClickedEventArgs e)
		{
			// Check if the observatory is tracking an object or not
			if (m_TrackComboBox.SelectedIndex != 0)
			{
				DialogResult dialogResult =
					MessageBox.Show(
					this, 
					String.Format(
						Properties.Resources.ChangePositionWarning,
						((AstronomicalObject)m_TrackComboBox.SelectedItem).Name,
						System.Environment.NewLine),
					Properties.Resources.WarningTitle, 
					
					MessageBoxButtons.YesNo, 
					MessageBoxIcon.Warning);

				if (dialogResult == DialogResult.No)
				{
					// If user doesnt want to stop tracking, then exit the method
					return;
				}
			}

			ChangePositionForm form = new ChangePositionForm();
			form.Azimuth = m_Observatory.Altazimuth.Azimuth;
			form.Altitude = m_Observatory.Altazimuth.Altitude;

			if (form.ShowDialog(this) == DialogResult.OK)
			{
				// Change the position of the observatory
				m_Observatory.ChangePosition(
					new Altazimuth(form.Altitude, form.Azimuth));

				// If we have manually set the altazimuth, then we cant be targeting an object anymore
				m_TrackComboBox.SelectedIndex = 0;
			}

			form.Dispose();
		}

		/// <summary>
		/// Handles the SelectedIndexChanged event of the m_TrackComboBox control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		private void m_TrackComboBox_SelectedIndexChanged(object sender, EventArgs e)
		{
			SetAstronomicalObjectBeingTracked();
		}

		/// <summary>
		/// Sets the astronomical object being tracked.
		/// </summary>
		private void SetAstronomicalObjectBeingTracked()
		{
			if (m_Observatory != null)
			{
				if (m_TrackComboBox.SelectedIndex == 0)
				{
					m_Observatory.AstronomicalObjectBeingTracked = null;
				}
				else
				{
					m_Observatory.AstronomicalObjectBeingTracked =
						((AstronomicalObject)m_TrackComboBox.SelectedItem).ID;
				}
			}
		}
	}
}
